package org.ichihamigua.video.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiniu.util.Md5;
import lombok.extern.slf4j.Slf4j;
import org.ichihamigua.common.framework.entity.Result;
import jakarta.annotation.Resource;
import org.ichihamigua.common.qiniucloud.service.QiniuObjectStoreService;
import org.ichihamigua.common.qiniucloud.vo.InitPartUploadResponseVo;
import org.ichihamigua.common.qiniucloud.vo.UploadPartVo;
import org.ichihamigua.common.video.dto.CommitVideoDto;
import org.ichihamigua.common.video.dto.CreateVideoDto;
import org.ichihamigua.common.video.dto.PushVideoDto;
import org.ichihamigua.common.video.entity.VideoCategoryBindEntity;
import org.ichihamigua.common.video.entity.VideoEntity;
import org.ichihamigua.common.video.entity.vo.VideoVo;
import org.ichihamigua.video.mapper.VideoMapper;
import org.ichihamigua.video.service.VideoCategoryBindService;
import org.ichihamigua.video.service.VideoService;
import org.ichihamigua.video.vo.CreateVideoResult;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.multipart.MultipartFile;
import vo.PageVo;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author rainkyzhong
 * @date 2023/10/29 1:29
 */
@Service
@Slf4j
public class VideoServiceImpl extends ServiceImpl<VideoMapper,VideoEntity> implements VideoService{

    @Resource
    private RedisTemplate<String,String> redisTemplate;

    //视频存储空间
    private static final String VIDEO_BUCKET_NAME = "ichihamigua";

    //上传任务ID
    private final static String REDIS_KEY_OF_UPLOAD_ID = "upload:id:";

    private final static String REDIS_KEY_OF_PART_NUMBER = "part:number:";

    private final static String REDIS_KEY_OF_PART_LENGTH = "part:length:";

    //七牛云对象存储服务
    @Resource
    private QiniuObjectStoreService qiniuObjectStoreService;

    @Resource
    private VideoCategoryBindService videoCategoryBindService;

    @Override
    public Result<?> createVideo(Long userId, String ipAddress, CreateVideoDto dto){

        //TODO 对用户进行一些检查

        ValueOperations<String,String> ops = redisTemplate.opsForValue();


        //创建视频记录
        VideoEntity video = new VideoEntity();
        video.setState(dto.getState());
        video.setCreateTime(LocalDateTime.now());
        video.setTitle(dto.getTitle());
        video.setIpCity(1L);
        video.setUserId(userId);
        if(!save(video)) return Result.fail("创建失败");


        //创建视频分类
        List<VideoCategoryBindEntity> binds = new ArrayList<>();
        for (Long categoryId : dto.getCategoryId()) {
            binds.add(new VideoCategoryBindEntity(video.getId(), categoryId));
        }
        videoCategoryBindService.saveBatch(binds);

        //开启视频上传任务
        Result<InitPartUploadResponseVo> res = qiniuObjectStoreService.initiateMultipartUpload(
                VIDEO_BUCKET_NAME,
                String.format("%d.mp4",video.getId())
        );
        if (!res.getSuccess()) {
            return res;
        }

        //视频分片
        int partLength = dto.getSize() > 1024*1024*10 ? 5 : 1;
        //保存在redis中
        redisTemplate.opsForValue().set(REDIS_KEY_OF_PART_LENGTH + video.getId(), String.valueOf(partLength));
        ops.set(
                REDIS_KEY_OF_UPLOAD_ID+video.getId(),
                res.getData().getUploadId(),
                Duration.of(
                        res.getData().getExpireAt() - System.currentTimeMillis()/1000,
                        ChronoUnit.SECONDS
                )
        );
        log.info("{}", res);
        return Result.success(new CreateVideoResult(video.getId(),partLength));
    }


    @Override
    public Result<?> pushVideo(MultipartFile videoFile, PushVideoDto dto) {
        Long userId = 1L;
        String uploadId = redisTemplate.opsForValue().get(REDIS_KEY_OF_UPLOAD_ID+dto.getVideoId());

        log.info("开始时间,{}", LocalDateTime.now());

        // 上传分片
        Result<UploadPartVo> uploadResult = qiniuObjectStoreService
                .uploadPart(VIDEO_BUCKET_NAME, String.format("%d.mp4",dto.getVideoId()),
                        uploadId, dto.getPartNumber(), videoFile);
        log.info("{}", uploadResult);
        if (!uploadResult.getSuccess()) {
            return uploadResult;
        }
        log.info("完成时间,{}", LocalDateTime.now());


        // 获取 Redis 中的 List 对象
        ListOperations<String, String> listOps = redisTemplate.opsForList();
        // 将 partNumber 存储到 List 中
        listOps.rightPush(REDIS_KEY_OF_PART_NUMBER + dto.getVideoId(),
                String.valueOf(dto.getPartNumber()));
        // 构造返回值
        return Result.success();
    }

    @Override
    public Result<?> saveVideo(CommitVideoDto dto) {
        //检查视频是否全部上传完成
        ListOperations<String, String> ops = redisTemplate.opsForList();
        ValueOperations<String, String> partLengthOps = redisTemplate.opsForValue();
        int partLength = Integer.parseInt(Objects.requireNonNull(partLengthOps.get(REDIS_KEY_OF_PART_LENGTH + dto.getVideoId())));
        Long uploadedSize = ops.size(REDIS_KEY_OF_PART_NUMBER + dto.getVideoId());

        //缺少分段，通知重传
        if (uploadedSize == null || uploadedSize < partLength) {
            List<String> range = ops.range(REDIS_KEY_OF_PART_NUMBER + dto.getVideoId(), 0, -1);
            boolean[] map = new boolean[partLength];
            if(range == null){
                return Result.fail("请重新上传").data("all");
            }
            for (String s : range) {
                int i = Integer.parseInt(s);
                map[i] = true;
            }
            List<Integer> lackPart = new ArrayList<>();
            for(int i = 0; i < map.length; i++){
                if(!map[i]) lackPart.add(i);
            }
            return Result.fail("请重新上传").data(lackPart);
        }

        String fileName = String.format("%d.mp4", dto.getVideoId());

        // 获取 Redis 中的 List 对象
        String uploadId = redisTemplate.opsForValue().get(REDIS_KEY_OF_UPLOAD_ID+dto.getVideoId());
        Result<JSONObject> res = qiniuObjectStoreService.completeMultipartUpload(VIDEO_BUCKET_NAME, fileName, uploadId);
        if(!res.getSuccess()){
            return res;
        }
        //保存链接
        VideoEntity video = new VideoEntity();
        video.setId(dto.getVideoId());
        video.setSrc("ic://"+fileName);
        updateById(video);

        //上传成功
        return Result.success("上传成功");
    }

    @Override
    public Result<PageVo<VideoVo>> getVideoList(Long categoryId, Integer pageNumber) {
        if(pageNumber == null){
            pageNumber = 1; //默认为第一页
        }
        List<VideoEntity> videoList = baseMapper.getVideoList(categoryId, (pageNumber - 1) * 10, 10);
        List<VideoVo> videoVos = new ArrayList<>();
        for (VideoEntity video : videoList) {
            videoVos.add(new VideoVo(
                    video.getId(),
                    video.getUserId(),
                    "用户",
                    video.getTitle(),
                    video.getSrc(),
                    video.getState(),
                    0,
                    0,
                    0
            ));
        }
        return Result.success(new PageVo<>(0L, pageNumber, videoVos));
    }
}
